/*
 * Parser.cpp
 */

#include "Parser.h"

Parser::Parser(const std::string &archivoCargado) {
	this->modoAccesoDisco = false;
	this->archivoCompleto << archivoCargado;
	this->numLineaLeida = 0;
}

Parser::Parser(const std::string &ruta, const std::string &nombre) {
	this->modoAccesoDisco = true;
	this->numLineaLeida = 0;
	// Armo la dirección completa al archivo y lo abro
	if(!ruta.empty() && !nombre.empty()) {
		std::string fileName(ruta);
		fileName.append(nombre);
		this->archivo.open(fileName.c_str(), std::ios::in);
	}
}

Parser::~Parser() {
	// Cierro el archivo si corresponde
	if(this->archivo.is_open())
		this->archivo.close();
}

bool Parser::quitarComentarios(std::string &lineaLeida) {
	// Quita comentarios largos
	bool abortar = false;
	size_t iniComentLarg = lineaLeida.find(INI_COMENT_LARGO);
	while(!abortar && iniComentLarg != std::string::npos) {
		// Busco el fin de comentario largo en la misma linea
		size_t finComentLarg = lineaLeida.find(FIN_COMENT_LARGO);
		if(finComentLarg != std::string::npos) {
			// Borro el comentario largo en la linea
			lineaLeida.erase(iniComentLarg,
					(finComentLarg + LONG_FIN_COMENT_LARGO) - iniComentLarg);
		} else {
			// Sigo leyendo el archivo hasta encontrar fin de comentario largo
			std::string bufferLectura;
			while(finComentLarg == std::string::npos
					&& this->leerLinea(bufferLectura)) {
				finComentLarg = bufferLectura.find(FIN_COMENT_LARGO);
			}
			if(finComentLarg != std::string::npos) {
				// Borro el inicio del coment de la 1er linea y agrego el final
				lineaLeida.erase(iniComentLarg);
				lineaLeida.append(bufferLectura.substr(finComentLarg
						+ LONG_FIN_COMENT_LARGO));
			} else
				abortar = true;  // Si no hay fin de comentario, aborto
		}
		iniComentLarg = lineaLeida.find(INI_COMENT_LARGO);
	}

	// Quita comentarios de una linea
	size_t iniComentLineal = lineaLeida.find(INI_COMENT_LINEA);
	if(iniComentLineal != std::string::npos) {
		lineaLeida = lineaLeida.substr(0, iniComentLineal);
	}
	return !abortar;
}

bool Parser::estadoValido() {
	if(this->modoAccesoDisco)
		return this->archivo.is_open();
	return (this->archivoCompleto.peek() > 0);
}

bool Parser::leerLinea(std::string &lineaLeida) {
	bool resultado = false;
	if(this->modoAccesoDisco) {
		// Intento leer una linea y devuelvo el resultado
		std::getline(this->archivo, lineaLeida);
		resultado = !this->archivo.eof();
	} else {
		std::getline(this->archivoCompleto, lineaLeida);
		resultado = (this->archivoCompleto.peek() > 0);
	}
	if(resultado)
		++this->numLineaLeida;
	return resultado;
}

bool Parser::parsearLinea(std::string &lineaLeida) {
	// Si se pudo leer, quito los comentarios de la linea
	if(this->leerLinea(lineaLeida))
		return this->quitarComentarios(lineaLeida);
	return false;
}

bool Parser::esDirectiva(const std::string &lineaLeida) const {
	if(!lineaLeida.empty())
		return (lineaLeida[0] == CHR_DIRECT_PRECOMP);
	return false;
}

std::string Parser::getDirectiva(const std::string &lineaLeida) const {
	// Separo la directiva de la linea
	size_t longDirect = lineaLeida.find_first_of(SEP_PALABRAS);
	if(longDirect == std::string::npos) {
		// Caso directiva sin parametros
		return lineaLeida.substr(1);
	}
	// Caso directiva con parametros
	return lineaLeida.substr(1, longDirect - 1);
}

std::string Parser::getValorDirectiva(const std::string &lineaLeida) const {
	// Extraigo el valor de la directiva de la lineaLeida
	size_t longDirect = lineaLeida.find_first_of(SEP_PALABRAS);
	if(longDirect == std::string::npos) {
		// Caso directiva sin parametros
		return std::string();
	}
	// Caso directiva con parametros
	return lineaLeida.substr(1 + longDirect);
}

std::string Parser::tokenizarDobleLado(const std::string &cadena,
		const std::string &token) const {
	size_t iniCadena = cadena.find(token);
	if(iniCadena != std::string::npos) {
		iniCadena += token.size();
		size_t finCadena = cadena.find(token, iniCadena);
		if(finCadena != std::string::npos) {
			return cadena.substr(iniCadena, finCadena - iniCadena);
		}
	}
	return std::string();
}

void Parser::reemplazarDefiniciones(std::string &bufferLectura,
		const TablaDefiniciones &tabDef) const {
	std::string salida;
	bool hayReemplazos = false;
	bool ultimoLexema = false;
	size_t posIni = 0;
	size_t posFin = bufferLectura.find_first_of(SEP_PALABRAS);
	size_t tamBuffer = bufferLectura.size();
	std::string lexemaOrig, lexemaReemplazado;
	while(posFin != std::string::npos) {
		lexemaOrig = bufferLectura.substr(posIni, posFin - posIni);
		if(tabDef.obtenerDefinicion(lexemaReemplazado, lexemaOrig)) {
			hayReemplazos = true;
			// Concateno el lexagrama con el reemplazo efectuado
			salida.append(lexemaReemplazado);
		} else {
			// Concateno el lexagrama original
			salida.append(lexemaOrig);
		}
		// Si no es el ultimo lexema, agrego el separador
		if(!ultimoLexema)
			salida.push_back(bufferLectura[posFin]);
		// Actualizo las posciones de inicio y fin
		posIni = posFin + 1;
		posFin = bufferLectura.find_first_of(SEP_PALABRAS, posIni);
		// Si es el ultimo lexema, asigno el fin de buffer a posFin
		if(posFin == std::string::npos && !ultimoLexema) {
			ultimoLexema = true;
			posFin = tamBuffer;
		}
	}
	// Si hubo algun reemplazo, actualizo el buffer de salida
	if(hayReemplazos)
		bufferLectura.assign(salida);
}

void Parser::reemplazarCharsReservadosHtml(std::string &linea) const {
	std::string salida;
	size_t posIni = 0;
	size_t posFin = linea.find_first_of(HTML_RESERVED);
	// Busco si hay algún caracter reservado en la linea pasada
	while (posFin != std::string::npos) {
		salida += linea.substr(posIni, posFin - posIni);
		std::stringstream ss;
		ss << (int) linea[posFin];
		salida += "&#" + ss.str() + ";";
		posIni = ++posFin;
		posFin = linea.find_first_of(HTML_RESERVED, posIni);
	}
	// Si hubo reemplazos, copio la salida a la entrada
	if (posIni > 0) {
		salida += linea.substr(posIni);
		linea.assign(salida);
	}
}

std::string Parser::getNumLineaLeida() const {
	std::stringstream ss;
	ss << this->numLineaLeida;
	return ss.str();
}
